#define PRINTK_BYTE_IO_VIRTUAL_ADDR 0xd0000020
 
#define PRINTK_IS_PRINTABLE(c)   ( ( (c >= 32) && (c <= 126) ) || (c == '\n') ) 
#define PRINTK_FMT_TYPE_FLAG '%'
#define PRINTK_FMT_TYPE_CHAR 'c'
#define PRINTK_FMT_TYPE_INT 'd'
#define PRINTK_FMT_TYPE_ADDR 'a'
#define PRINTK_FMT_TYPE_STRING 's'
 
#define PRINTK_PRINT_CHAR(c) do { \
	if (PRINTK_IS_PRINTABLE(c)) \
	{ \
		*(volatile unsigned int *)(PRINTK_BYTE_IO_VIRTUAL_ADDR) = c; \
	} \
}while(0);
 
#define PRINTK_STRUCT_BUF_SIZE 64
struct
{
	char buf[PRINTK_STRUCT_BUF_SIZE];
	unsigned int amount;
}printk_struct;
 
#define PRINTK_TEMP_STRUCT_BUF_SIZE 32
struct
{
	char buf[PRINTK_TEMP_STRUCT_BUF_SIZE];
	unsigned int amount;
}printk_temp_struct;
 
void printk_struct_buf_clear(void)
{
	unsigned int i = 0;
	for (i = 0; i < PRINTK_STRUCT_BUF_SIZE; i++)
	{
		printk_struct.buf[i] = '\0';
	}
	printk_struct.amount = 0;
}
 
void printk_temp_struct_buf_clear(void)
{
	unsigned int i = 0;
	for (i = 0; i < PRINTK_TEMP_STRUCT_BUF_SIZE; i++)
	{
		printk_temp_struct.buf[i] = '\0';
	}
	printk_temp_struct.amount = 0;
}
 
void printk_struct_buf_insert_tail(char c)
{
	if (PRINTK_IS_PRINTABLE(c))
	{
		if (printk_struct.amount < PRINTK_STRUCT_BUF_SIZE)
		{
			printk_struct.buf[printk_struct.amount] = c;
			printk_struct.amount++;
		}
	}
}
 
void printk_temp_struct_buf_insert_tail(char c)
{
	if (PRINTK_IS_PRINTABLE(c))
	{
		if (printk_temp_struct.amount < PRINTK_TEMP_STRUCT_BUF_SIZE)
		{
			printk_temp_struct.buf[printk_temp_struct.amount] = c;
			printk_temp_struct.amount++;
		}
	}
}
 
void printk_print_string(char *string)
{
	char *p = string;
	unsigned char i = 0;
	for (;;)
	{
		if (PRINTK_IS_PRINTABLE(p[i]))
		{
			PRINTK_PRINT_CHAR(p[i]);
		}
		else
		{
			break;
		}
		i++;
	}
}
 
unsigned int printk_get_string_length(char *string)
{
	char *p = string;
	unsigned int length = 0;
	for (;;)
	{
		if (PRINTK_IS_PRINTABLE(p[length]))
		{
			length++;
			continue;
		}
		else
		{
			return(length);
		}
	}
}
 
void printk_temp_struct_buf_reverse(void)
{
	unsigned int i = 0;
	char temp = '\0';
	for (i = 0; i < printk_temp_struct.amount; i++)
	{
		if (i >= (printk_temp_struct.amount - 1 - i))
		{
			break;
		}
		temp = printk_temp_struct.buf[i];
		printk_temp_struct.buf[i] = printk_temp_struct.buf[printk_temp_struct.amount - 1 - i];
		printk_temp_struct.buf[printk_temp_struct.amount - 1 - i] = temp;
	}
}
 
void printk_temp_struct_buf_fill_int(int num_int)
{
	int num_int_positve = 0;
	char result = '\0';
	if (num_int == 0)
	{
		printk_temp_struct_buf_insert_tail('0');
		return;
	}
	else if (num_int > 0)
	{
		num_int_positve = num_int;
	}
	else if (num_int < 0)
	{
		num_int_positve = -num_int;
	}
	
	for (;;)
	{
		if (num_int_positve == 0)
		{
			break;
		}
		result = (char)(num_int_positve % 10) + '0';
		printk_temp_struct_buf_insert_tail(result);
		num_int_positve = num_int_positve / 10;
	}
	if (num_int < 0)
	{
		printk_temp_struct_buf_insert_tail('-');
	}
	printk_temp_struct_buf_reverse();
}
 
void printk_struct_buf_append_temp_buf(void)
{
	unsigned int i = 0;
	char *p = printk_temp_struct.buf;
	for (i = printk_struct.amount;i < PRINTK_STRUCT_BUF_SIZE; i++)
	{
		if (PRINTK_IS_PRINTABLE(*p))
		{
			printk_struct_buf_insert_tail(*p);
		}
		else
		{
			break;
		}
		p++;
	}
}
 
void printk_temp_struct_buf_fill_addr(unsigned int num_addr)
{
	unsigned int addr = num_addr;
	char result = '\0';
 
	if (num_addr == 0)
	{
		printk_temp_struct_buf_insert_tail('0');
		return;
	}
 
	for (;;)
	{
		if (addr == 0)
		{
			break;
		}
		result = (char)(addr % 10) + '0';
		printk_temp_struct_buf_insert_tail(result);
		addr = addr / 10;
	}
	printk_temp_struct_buf_reverse();
}
 
void printk_temp_struct_buf_fill_string(unsigned int char_addr)
{
	char *p = (char *)char_addr;
	for (;;)
	{
		if (PRINTK_IS_PRINTABLE(*p))
		{
			printk_temp_struct_buf_insert_tail(*p);
		}
		else
		{
			break;
		}
		p++;
	}
}
 
 
#define PRINTK_INIT_PARGV(pArgv,fmt) ( pArgv = (char *)&fmt )
#define PRINTK_REFRESH_PARGV(pArgv) ( pArgv = pArgv + 4 )
#define PRINTK_GET_ARGV(pArgv,type) ( *(type *)pArgv )
#define PRINTK_DEINIT_PARGV(pArgv) ( pArgv = (char*)0 )
void printk_struct_buf_format(char *pV,char *fmt)
{
	char *pArgv = pV;
	char *format = fmt;
	unsigned int format_length = 0;
	unsigned int i = 0;
	format_length = printk_get_string_length(format);
	for (i = 0; i < format_length; i++)
	{
		if (format[i] == PRINTK_FMT_TYPE_FLAG)
		{
			if (format[i + 1] == PRINTK_FMT_TYPE_CHAR)
			{
				PRINTK_REFRESH_PARGV(pArgv);
				char c = PRINTK_GET_ARGV(pArgv,char);
				printk_struct_buf_insert_tail(c);
				i++;
			}
			else if (format[i + 1] == PRINTK_FMT_TYPE_INT)
			{
				printk_temp_struct_buf_clear();
				PRINTK_REFRESH_PARGV(pArgv);
				int num_int = PRINTK_GET_ARGV(pArgv,int);
				printk_temp_struct_buf_fill_int(num_int);
				printk_struct_buf_append_temp_buf();
				i++;
			}
			else if (format[i + 1] == PRINTK_FMT_TYPE_ADDR)
			{
				printk_temp_struct_buf_clear();
				PRINTK_REFRESH_PARGV(pArgv);
				unsigned int num_addr = PRINTK_GET_ARGV(pArgv,unsigned int);
				printk_temp_struct_buf_fill_addr(num_addr);
				printk_struct_buf_append_temp_buf();
				i++;
			}
			else if (format[i + 1] == PRINTK_FMT_TYPE_STRING)
			{
				printk_temp_struct_buf_clear();
				PRINTK_REFRESH_PARGV(pArgv);
				unsigned int char_addr = PRINTK_GET_ARGV(pArgv,unsigned int);
				printk_temp_struct_buf_fill_string(char_addr);
				printk_struct_buf_append_temp_buf();
				i++;
			}
			else
			{
				printk_struct_buf_insert_tail(format[i]);
			}
		}
		else
		{
			printk_struct_buf_insert_tail(format[i]);
		}
	}
 
}
 
void printk(char *fmt,...)
{
	printk_struct_buf_clear();
	char *pArgv;
	PRINTK_INIT_PARGV(pArgv,fmt);
	printk_struct_buf_format(pArgv,fmt);
	printk_print_string(printk_struct.buf);
	PRINTK_DEINIT_PARGV(pArgv);
}
 
void test_printk(void)
{
	printk("test_printk start\n");
	int x;
	printk("[char:%c,int:%d,int:%d,int:%d,address:%a,address:%a,string:%s]\n",'a',123,-123,0,0,&x,"hello_printk");
	printk("test_printk end\n");
}